/*
 * Copyright (c) 2011, 9nali.com All rights reserved.
 */
package com.waitingsnow.core.display.control
{
import flash.display.DisplayObject;
import flash.display.InteractiveObject;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.utils.Dictionary;
import flash.utils.Timer;

/**
 * The ToolTipManager lets you set basic ToolTip functionality, such as display delay and the disabling of ToolTips.
 * @author Sean Zou
 *
 */
public class ToolTipManager extends EventDispatcher
{

    /**
     * Define the faet mode display tooltip time.
     */
    public static var FAST_DISPLAY_TIME:uint = 10;

    /**
     * Define the tooltip max display time.
     */
    public static var MAX_DISPLAY_TIME:uint = 10;

    /**
     * Define reset the fast mode time.
     */
    public static var QUASH_TIME:uint = 10;
    //==========================================================================
    //  Class variables
    //==========================================================================
    /**
     * Define mouse over target this time start show tooltip.
     */
    public static var TRIGG_TIME:uint = 1000;

    /**
     *  @private
     */
    private static var instance:ToolTipManager;

    //==========================================================================
    //  Class methods
    //==========================================================================

    /**
     *  Returns the sole instance of this singleton class,
     *  creating it if it does not already exist.
     */
    public static function getInstance():ToolTipManager
    {
        if (!instance)
        {
            instance = new ToolTipManager();
        }
        return instance;
    }

    //==========================================================================
    //  Constructor
    //==========================================================================

    /**
     * Constructs a singleton <code>ToolTipManager</code> instance.
     *
     */
    public function ToolTipManager()
    {
        super();

        if (instance)
        {
            throw new Error(instance);
        }

        toolTipMap = new Dictionary(true);

        triggTimer = new Timer(TRIGG_TIME, 1);
        triggTimer.addEventListener(TimerEvent.TIMER_COMPLETE, triggTimeConplete);

        quashTimer = new Timer(QUASH_TIME, 1);
        quashTimer.addEventListener(TimerEvent.TIMER_COMPLETE, quashTimeConplete);
    }

    //----------------------------------
    //  enabled
    //----------------------------------
    private var _enabled:Boolean = true;

    //==========================================================================
    //  Properties
    //==========================================================================
    //----------------------------------
    //  toolTipDisplay
    //----------------------------------
    private var _toolTipDisplay:ToolTipDisplay = new ToolTipDisplay();

    /* current show tooltip target */
    private var currentTarget:InteractiveObject;

    /* is use fast mode display tooltip */
    private var isFastDisplay:Boolean = false;

    /* quash fast mode timer */
    private var quashTimer:Timer;

    //==========================================================================
    //  Variables
    //==========================================================================
    /* all need tooltip objects */
    private var toolTipMap:Dictionary;

    /* trigg tooltip timer */
    private var triggTimer:Timer;

    //==========================================================================
    //  Methods
    //==========================================================================
    /**
     * Add a tooltip target to manager.
     * @param target spring the tooltip target.
     * @param toolTip the show info.
     *
     */
    public function addToolTip(target:InteractiveObject, toolTip:* = null):void
    {
        if (target)
        {
            if (toolTipMap[target] == null)
            {
                target.addEventListener(MouseEvent.MOUSE_OVER, mouseOverHanlder, false, 0, true);
                target.addEventListener(MouseEvent.MOUSE_OUT, mouseOutHanlder, false, 0, true);
                target.addEventListener(Event.REMOVED, removedHandler, false, 0, true);
            }

            if (target.hasOwnProperty("toolTip"))
            {
                toolTipMap[target] = target["toolTip"];
            }
            else if (toolTip)
            {
                toolTipMap[target] = toolTip;
            }
        }
    }

    /**
     * If true, the ToolTipManager will automatically show ToolTips when the user moves the mouse pointer over components.
     * If false, no ToolTips will be shown.
     * @return
     *
     */
    public function get enabled():Boolean
    {
        return _enabled;
    }

    public function set enabled(value:Boolean):void
    {
        _enabled = value;
    }

    /**
     * Remove a tooltip target.
     * @param target will remove tooltip target
     *
     */
    public function removeToolTip(target:InteractiveObject):void
    {
        if (target && toolTipMap[target] != null)
        {
            if(currentTarget == target)
            {
                mouseOutHanlder(null);
            }
            
            target.removeEventListener(MouseEvent.MOUSE_OVER, mouseOverHanlder);
            target.removeEventListener(MouseEvent.MOUSE_OUT, mouseOutHanlder);
            target.removeEventListener(Event.REMOVED, mouseOutHanlder);
            delete toolTipMap[target];
        }
    }

    /**
     * Define the tooltip view.
     * @return
     *
     */
    public function get toolTipDisplay():ToolTipDisplay
    {
        return _toolTipDisplay;
    }

    public function set toolTipDisplay(value:ToolTipDisplay):void
    {
        _toolTipDisplay = value;
    }

    private function mouseOutHanlder(event:Event):void
    {
        if (toolTipDisplay && DisplayObject(toolTipDisplay).stage)
        {
            DisplayObject(toolTipDisplay).stage.removeChild(DisplayObject(toolTipDisplay));
        }

        triggTimer.stop();
        quashTimer.stop();
        quashTimer.reset();
        quashTimer.start();

        currentTarget = null;
    }

    private function removedHandler(event:Event):void
    {
        if (event.currentTarget != event.target || !currentTarget ||
            currentTarget != event.currentTarget)
        {
            //防止受子对象冒泡出来的removed事件影响
            return;
        }

        if (toolTipDisplay && DisplayObject(toolTipDisplay).stage)
        {
            DisplayObject(toolTipDisplay).stage.removeChild(DisplayObject(toolTipDisplay));
        }

        triggTimer.stop();
        quashTimer.stop();
        quashTimer.reset();
        quashTimer.start();

        currentTarget = null;
    }

    //==========================================================================
    //  Event handlers
    //==========================================================================
    private function mouseOverHanlder(event:MouseEvent):void
    {
        if (!enabled)
            return;
        currentTarget = InteractiveObject(event.currentTarget);
        if (currentTarget.stage)
        {
            triggTimer.stop();

            if (isFastDisplay)
            {
                triggTimer.delay = FAST_DISPLAY_TIME;
            }
            else
            {
                triggTimer.delay = TRIGG_TIME;
            }

            triggTimer.reset();
            triggTimer.start();
        }
    }

    private function quashTimeConplete(event:TimerEvent):void
    {
        isFastDisplay = false;
    }

    private function triggTimeConplete(event:TimerEvent):void
    {
        isFastDisplay = true;
        if (currentTarget && currentTarget.stage)
        {
            var toolTipTemp:DisplayObject = DisplayObject(toolTipDisplay);

            toolTipDisplay.display(toolTipMap[currentTarget]);
            currentTarget.stage.addChild(toolTipTemp);

            var tx:int = currentTarget.stage.mouseX;
            var ty:int = currentTarget.stage.mouseY + 20;
            if (toolTipTemp.width + tx > currentTarget.stage.stageWidth)
            {
                tx = currentTarget.stage.stageWidth - toolTipTemp.width - 3;
            }
            if (toolTipTemp.height + ty > currentTarget.stage.stageHeight)
            {
                ty = currentTarget.stage.stageHeight - toolTipTemp.height - 30;
            }
            toolTipTemp.x = tx;
            toolTipTemp.y = ty;
        }
    }
}
}

